/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package meteocal.control;

import java.util.Calendar;
import java.util.List;
import javax.ejb.Stateless;
import javax.inject.Inject;
import meteocal.entity.Event;
import meteocal.entity.Invitation;
import meteocal.entity.InviteNotification;
import meteocal.entity.RescheduleNotification;
import meteocal.entity.Status;
import meteocal.entity.Users;
import meteocal.entityManager.EventManager;
import meteocal.entityManager.InvitationManager;
import meteocal.entityManager.UserManager;
import meteocal.exception.InvalidDateException;
import meteocal.exception.EmptyFieldException;
import meteocal.exception.InvalidOperationException;
import meteocal.weather.Weather;

/**
 *
 * @author andrea
 */
@Stateless
public class EventControl {
    
    @Inject
    EventManager em;
    
    @Inject
    UserManager um;
    
    @Inject
    InvitationManager im;
    
    @Inject
    NotificationControl nc;
    
    /**
     * Creates a new event if the dates are consistent.
     * @param event
     * @throws InvalidDateException if the starting time is after the ending time
     * or if it is scheduled in the past.
     * @throws EmptyFieldException if at least one of the required field is empty.
     */
    public void createEvent(Event event) throws InvalidDateException, EmptyFieldException{
        if(!checkFields(event)){
            throw new EmptyFieldException();
        }
        if(checkDate(event)){
            if(event.getBadWeather() == null)
                event.setBadWeather(Weather.RAINY);
            em.save(event);
            return;
        }
        throw new InvalidDateException();
    }
    
    /**
     * Modifies an event in the database if the new informations are consistent.
     * @param event
     * @throws InvalidDateException if the new dates aren't consistent.
     * @throws InvalidOperationException if the logged user isn't the creator of
     * the event.
     */
    public void editEvent(Event event) throws InvalidDateException, InvalidOperationException{
        if(!isCreator(event))
            throw new InvalidOperationException();
        if(checkDate(event)){
            if(!checkFields(event)){
                throw new InvalidOperationException();
            }
            em.update(event);
            RescheduleNotification not = buildRescheduleNotification(event);
            nc.sendNotification(not, em.getParticipants(event));
        }else{
            throw new InvalidDateException();
        }
    }
    
    /**
     * Removes the event from the logged user's calendar. If he is the creator
     * of the event, this one is removed also from the invited users' calendars
     * and the users are notified about the deletion.
     * @param event
     */
    public void removeEvent(Event event){
        if(!isCreator(event)){
            removeFromCalendar(event);
        } else {
            List<Invitation> invited = im.findByEvent(event);
            for(Invitation i : invited){
                removeFromCalendar(event, i.getUser());              
            }
            em.delete(event);
        }
    }
    
    /**
     * Creates the invitations for the users added to the event and notifies
     * them.
     * @param emails
     * @param event
     */
    public void inviteUsers(List<String> emails, Event event){
        List<Users> users = um.usersFromString(emails);
        for(int i=0;i<users.size();i++){
            if(!isInvited(users.get(i), event)){
                Invitation inv = new Invitation();
                inv.setEvent(event);
                inv.setUser(users.get(i));
                inv.setStatus(Status.PENDING);
                im.save(inv);
            } else {
                Invitation invit = im.find(users.get(i), event);
                if(invit.getStatus().equals(Status.REFUSED)){
                    invit.setStatus(Status.PENDING);
                    im.update(invit);
                }else{
                    users.remove(users.get(i));
                    i--;
                }
            }
        }
        InviteNotification not = buildInviteNotification(event);
        nc.sendNotification(not, users);
    }
    
    /**
     * Checks if the currently logged user is the creator of the event.
     * @param event
     * @return true if he is, false otherwise
     */
    public boolean isCreator(Event event){
        return event.getCreator().equals(um.getLoggedUser());
    }
    
    /**
     * Checks if the currently logged user is invited to the event
     * @param event
     * @return true if he is, false otherwise
     */
    public boolean isInvited(Event event){
        return em.getInvited(event).contains(um.getLoggedUser());
    }
    
    /**
     * Removes the event from the logged user's calendar.
     * @param event
     */
    private void removeFromCalendar(Event event){
        Invitation inv = im.find(um.getLoggedUser(), event);
        if(inv!=null){
            im.delete(inv);
            nc.removeNotification(event, um.getLoggedUser());
        }
    }
    
    /**
     * Removes the event from the given user's calendar.
     * @param event
     * @param user
     */
    private void removeFromCalendar(Event event, Users user){
        Invitation inv = im.find(user, event);
        if(inv!=null){
            im.delete(inv);
            nc.removeNotification(event, user);
        }
    }
    
    /**
     * Checks if the starting date is before the ending date and after
     * the current date.
     * @param event
     * @return true if the dates are consistent, false otherwise.
     */
    private boolean checkDate(Event event){
        Calendar today = Calendar.getInstance();
        Calendar start = event.getStartTime();
        Calendar end = event.getEndTime();
        return !(today.after(start) || end.before(start));
    }
    
    /**
     * Checks if there are any empty field that must be fulfilled.
     * @param event
     * @return true if there are no empty fields, false otherwise.
     */
    private boolean checkFields(Event event){
        return event.getPlace() != null && event.getTitle() != null && !event.getTitle().isEmpty() && !event.getPlace().isEmpty();
    }
    
    /**
     * Checks if the user is already invited to the event
     * @param user
     * @param event
     * @return true if he is invited, false otherwise
     */
    private boolean isInvited(Users user, Event event){
        for(Invitation i : em.getInvitations(event)){
            if(i.getUser().equals(user))
                return true;
        }
        return false;
    }
    
    /**
     * Builds the notification related to the event invitation.
     * @param event
     * @return the notification
     */
    private InviteNotification buildInviteNotification(Event event){
        InviteNotification not = new InviteNotification();
        not.setEvent(event);
        not.setOrganizer(event.getCreator());
        not.setSeen(false);
        return not;
    }
    
    /**
     * Builds the notification with new event informations after rescheduling.
     * @param event
     * @return the notification
     */
    private RescheduleNotification buildRescheduleNotification(Event event){
        RescheduleNotification not = new RescheduleNotification();
        not.setEvent(event);
        not.setNewDate(event.getStartTime());
        not.setNewPlace(event.getPlace());
        not.setNewTitle(event.getTitle());
        not.setSeen(false);
        return not;
    }
    
}
